/* =========================================================================
 *
 *   The traffic processor.
 *
 *   Development by Sedelnikov Alexander (Bigking).
 *
 * =========================================================================
 */

#include "transact.h"
#include <sys/stat.h>
#include <dirent.h>
#include "log.h"

#ifdef POSTGRES
    #include<libpq-fe.h>
#endif


//-----------------------------------------------------------------------//
ctransaction::ctransaction()
{
}


//-----------------------------------------------------------------------//
ctransaction::~ctransaction()
{
    for( int i = 0; i < files.size(); i++ )
        delete []((char*)files[ i ]);
    files.clear();
}


//-----------------------------------------------------------------------//
void ctransaction::append_file( const char *src_name,
                                const char *arhive_name )
{
    char  *fn;
    fn = new char[ strlen( src_name ) + 0x10 ];
    strcpy( fn, src_name );
    files.push_back( fn );

    fn = new char[ strlen( arhive_name ) + 0x10 ];
    strcpy( fn, arhive_name );
    files.push_back( fn );
}


//-----------------------------------------------------------------------//
void ctransaction::generate_uid()
{
    sprintf( unical_identifier, "%04X%04X%04X", rand(), rand(), rand() );
}


//-----------------------------------------------------------------------//
const char* ctransaction::get_unical_id( ) const
{
    return unical_identifier;
}


//-----------------------------------------------------------------------//
void ctransaction::begin()
{
    char  full_path[ 2048 ] = { 0 };
    FILE *fp;

    generate_uid();

    sprintf( full_path, "%s/tp_%s.tr_begin",
             cconfig::get_transaction_folder(),
             unical_identifier );

    fp = fopen( full_path, "wb" );
    if( fp == NULL )
        THROW( e_error, "Can't create file for transaction [%s]", full_path );

    fprintf( fp, "%ld\n", files.size() );
    fprintf( fp, "%s\n", unical_identifier );
    fflush( fp );

    for( int i = 0; i < files.size(); i++ )
        fprintf( fp, "%s\n", (char*)files[ i ] );

    fflush( fp );
    fclose( fp );
}


//-----------------------------------------------------------------------//
void ctransaction::end()
{
    FILE *fp;

    char  full_path[ 2048 ] = { 0 }, text[ 256 ];

    sprintf( full_path, "%s/tp_%s.tr_end",
             cconfig::get_transaction_folder(),
             unical_identifier );

    fp = fopen( full_path, "wb" );
    if( fp == NULL )
        THROW( e_error, "Can't create file for transaction [%s]", full_path );
    fflush( fp );
    fclose( fp );

    sprintf( full_path, "%s/tp_%s.tr_begin",
             cconfig::get_transaction_folder(),
             unical_identifier );

    remove( full_path );

    sprintf( full_path, "%s/tp_%s.tr_end",
             cconfig::get_transaction_folder(),
             unical_identifier );

    remove( full_path );
}


//-----------------------------------------------------------------------//
void ctransaction::check_xlog( )
{
//    logmsg( "Hello" );

#ifdef POSTGRES
    PGconn   *pgsql;
    PGresult *pgres;

    if( !strcmp( cconfig::get_out_stream(), "pgsql" ) )
    {
        pgsql = PQconnectdb( cconfig::get_out_server() );
        if( pgsql == NULL || PQstatus( pgsql ) == CONNECTION_BAD )
        {
            if( pgsql )
                PQfinish( pgsql );
            pgsql = NULL;

            THROW( e_error, "Can't connect to the database!" );
        }
    }
#endif
    // Algorithm:
    //  Scan transaction folder for N.tr_begin [N]-Some name!
    //    if N.tr_end not exist then
    //       check N.tr_begin on demage -> if demage then remove N.tr_begin
    //       [Copy from arhive, and create .downloaded] if ->
    //           check UID in transaction table (for DB) ->
    //             if exist -> remove N.tr_begin, remove in transaction table
    //             else     -> [Copy from arhive] = TRUE
    //           other (not for DB)
    //             [Copy from arhive] = TRUE
    //    else
    //       remove N.tr_begin N.tr_end and in transaction table UID!


    char         full_path[ 2048 ],
                 tr_b[ 2048 ],
                 tr_e[ 2048 ],
                 path_src [ 2048 ],
                 path_dst [ 2048 ],
                 text     [ 2048 ];
    char         uid[ 256 ];

    DIR         *dir_ptr  = opendir( cconfig::get_transaction_folder() );

    if( dir_ptr == NULL )
    {
        THROW( e_error, "Transaction folder not exists! [%s]",
               cconfig::get_transaction_folder() );
    }

    dirent      *dir_item = NULL;
    FILE        *fp;
    int          lines, count;
    ulong        files_seek;

    //  Scan transaction folder for N.tr_begin [N]-Some name!
    while( ( dir_item = readdir( dir_ptr ) ) != NULL )
    {
        if( !file_extension_check( dir_item->d_name, ".tr_begin" ) )
            continue;

        //    if N.tr_end exist then
        sprintf( tr_e, "%s/%s.tr_end",
                 cconfig::get_transaction_folder(),
                 file_basename( dir_item->d_name, text, 2000 ) );

        sprintf( tr_b, "%s/%s",
                 cconfig::get_transaction_folder(), dir_item->d_name );

        if( file_exist( tr_e ) )
        {
            remove( tr_b );
            remove( tr_e );

            logdbg( "[%s] Finished transaction log was removed!", tr_b );

            continue;
        }


        //       check N.tr_begin on demage -> if demage then remove N.tr_begin
        lines = 0;

        fp = fopen( tr_b, "r" );

        fscanf( fp, "%ld\n", &lines );
        fgets( uid, 255, fp );
        uid[ strlen( uid ) - 1 ] = 0;


        count = -1;
        while( !feof( fp ) )
        {
            fgets( text, 2047, fp );
            count++;
        }

        if( lines != count )
        {
            remove( tr_b );
//            remove( tr_e );
            fclose( fp );

            logdbg( "[%s] demaged transaction log was removed!", tr_b );

            continue;
        }


        //       [Copy from arhive, and create .downloaded] if ->
        //           check UID in transaction table (for DB) ->
        //             if exist -> remove N.tr_begin, remove in transaction table
        //             else     -> [Copy from arhive] = TRUE
        //           other (not for DB)
        //             [Copy from arhive] = TRUE
        if( !strcmp( cconfig::get_out_stream(), "pgsql" ) )
        {
#ifndef POSTGRES
            THROW( e_error, "This application is not compiled with Postgres!" );
#else
            sprintf( text, "SELECT ident FROM %s WHERE ident = '%s' LIMIT 1",
                     cconfig::get_transaction_table(),
                     uid );

            pgres = PQexec( pgsql, text );

            if( pgres == NULL )
            {
                THROW( e_error, "Can't select from the database." );
            }
            if( PQresultStatus( pgres ) == PGRES_NONFATAL_ERROR ||
                               PQresultStatus( pgres ) == PGRES_FATAL_ERROR )
            {
                THROW( e_error, "Can't select from the database." " [%s]", PQresultErrorMessage( pgres ) );
            }

            if( PQntuples( pgres ) != 0 )
            {
                remove( tr_b );
                PQclear( pgres );
                fclose( fp );

                logdbg( "[%s] Not finished transaction (table) was removed!", tr_b );

                continue;
            }

            PQclear( pgres );
#endif
        }

        fseek( fp, 0, 0 );
        fscanf( fp, "%ld\n", &lines );
        fgets( uid, 255, fp );

        // copy from arhive to src dir
        for( int i = 0; i < lines; i += 2 )
        {
            fgets( path_dst, 2047, fp );
            fgets( path_src, 2047, fp );

            path_src[ strlen( path_src ) - 1 ] = 0;
            path_dst[ strlen( path_dst ) - 1 ] = 0;

            file_copy( path_src, path_dst );
        }

        fseek( fp, 0, 0 );
        fscanf( fp, "%ld\n", &lines );
        fgets( uid, 255, fp );

        // create files
        for( int i = 0; i < lines; i += 2 )
        {
            fgets( path_src, 2047, fp );
            fgets( path_src, 2047, fp );

            path_src[ strlen( path_src ) - 1 ] = 0;

            int  index  = 0,
                 jindex = 0;
            for( index = strlen( path_src ) - 1; index >= 0; index-- )
                if( path_src[ index ] == '/' )
                {
                    index++;
                    break;
                }

            path_dst[ 0 ] = 0;

            for( jindex = 0; path_src[ index + jindex ] != 0; jindex++ )
                path_dst[ jindex ] = path_src[ index + jindex ];

            path_dst[ jindex ] = path_src[ index + jindex ];

            sprintf( path_src, "%s/%s.downloaded",
                     cconfig::get_source_folder(),
                     path_dst );

            FILE *fp_dwn = fopen( path_src, "wb" );

            if( fp_dwn == NULL )
                THROW( e_error, "Can't create downloaded file [%s]", path_src );
            fflush( fp_dwn );

            fclose( fp_dwn );
        }

        fclose( fp );

        remove( tr_b );
    }

    closedir( dir_ptr );

    if( !strcmp( cconfig::get_out_stream(), "pgsql" ) )
    {
#ifndef POSTGRES
        THROW( e_error, "This application is not compiled with Postgres!" );
#else
        // clear all transactions in the table transaction.
        sprintf( text, "TRUNCATE %s;",
                 cconfig::get_transaction_table(),
                 uid );

        pgres = PQexec( pgsql, text );

        if( pgres == NULL )
        {
            THROW( e_error, "Can't truncate transaction table." );
        }

        if( PQresultStatus( pgres ) == PGRES_NONFATAL_ERROR ||
                           PQresultStatus( pgres ) == PGRES_FATAL_ERROR )
        {
            THROW( e_error, "Can't truncate transaction table." " [%s]", PQresultErrorMessage( pgres ) );
        }

        PQclear( pgres );
        PQfinish( pgsql );
#endif
    }

}
